import Utils from './utils';

const DB_NAME = 'monitor'; // 数据库名称
const DB_VERSION = 1; // 版本升级用于schema变更
const MAX_RETRY_ATTEMPTS = 3; // 最大重试次数

export class DBUtils {
	db: IDBDatabase | null = null;
	table = {
		queue: [
			{
				name: 'data',
			},
			{
				name: 'hash',
				unique: true, // 确保哈希值唯一
			},
			{
				name: 'type',
			},
		],
		cache: [
			{
				name: 'hash',
				unique: true,
			},
		],
	};
	constructor() {
		this.initialize();
	}

	private initialize() {
		let attempts = 0;
		const tryOpenDB = () => {
			try {
				const request = window.indexedDB.open(DB_NAME, DB_VERSION);
				request.onupgradeneeded = event => {
					this.db = (event.target as IDBOpenDBRequest).result as IDBDatabase;
					this.createTable();
				};

				request.onsuccess = event => {
					this.db = (event.target as IDBOpenDBRequest).result as IDBDatabase;
					Utils.log('数据库初始化成功');

					// 监听数据库关闭事件
					this.db.onclose = () => {
						Utils.log('warn', '数据库连接意外关闭');
						this.db = null;
					};

					// 监听数据库版本变更
					this.db.onversionchange = () => {
						Utils.log('数据库版本变更，关闭连接');
						this.db?.close();
					};
				};

				request.onerror = (event: Event) => {
					attempts++;
					Utils.log(
						'warn',
						'数据库初始化失败',
						(event.target as IDBRequest).error
					);

					if (attempts < MAX_RETRY_ATTEMPTS) {
						Utils.log(
							'warn',
							`尝试重新连接数据库 (${attempts}/${MAX_RETRY_ATTEMPTS})`
						);
						tryOpenDB();
					} else {
						Utils.log('warn', '数据库初始化失败，重试次数已用完');
					}
				};
			} catch (error) {
				attempts++;
				Utils.log('warn', '数据库初始化异常', error);
				if (attempts < MAX_RETRY_ATTEMPTS) {
					Utils.log(
						'warn',
						`尝试重新连接数据库 (${attempts}/${MAX_RETRY_ATTEMPTS})`
					);
					tryOpenDB();
				} else {
					Utils.log('warn', '数据库初始化失败，重试次数已用完');
				}
			}
		};
		tryOpenDB();
	}

	private createTable() {
		Object.keys(this.table).forEach(tableName => {
			if (!this.db?.objectStoreNames.contains(tableName)) {
				const tableStore = this.db?.createObjectStore(tableName, {
					keyPath: 'id',
					autoIncrement: true,
				});
				this.table[tableName as keyof typeof this.table].forEach(_ => {
					tableStore?.createIndex(`${_.name}_idx`, _.name, {
						unique: !!_.unique,
					});
				});
				tableStore?.createIndex('created_at_idx', 'createdAt');
				tableStore?.createIndex('updated_at_idx', 'updatedAt');
			}
		});
	}

	private getTableStore(
		tableName: 'queue' | 'cache',
		mode: 'readonly' | 'readwrite' = 'readwrite'
	) {
		const transaction = this.db?.transaction([tableName], mode);
		const tableStore = transaction?.objectStore(tableName);
		return tableStore;
	}

	private requestHandler(request: IDBRequest<any> | undefined) {
		if (request) {
			request.onsuccess = function () {
				Utils.log('debug', 'DB操作成功');
			};
			request.onerror = function (event) {
				const error = (event.target as IDBRequest).error;
				if (error?.name === 'ConstraintError') {
					Utils.log(
						'warn',
						'数据违反唯一性约束，索引名:' +
							error.message.match(/index '(.+?)'/)?.[1]
					);
					// 这里可以添加您的冲突处理逻辑
				} else {
					Utils.log('warn', '操作出错', (event.target as IDBRequest).error);
				}
			};
		}
	}
	insert(params: { [key: string]: any }) {
		const tableStore = this.getTableStore('queue');
		const hash = Utils.generateUniqueHash(params);
		if (tableStore) {
			const addRequest = tableStore.add({
				type: params.type,
				hash,
				data: JSON.stringify(params),
				createdAt: Date.now(),
				updatedAt: Date.now(),
			});
			this.requestHandler(addRequest);
		}
	}

	deleteByIds(ids: Array<string>) {
		const tableStore = this.getTableStore('queue');
		if (tableStore) {
			ids.forEach(id => {
				const deleteRequest = tableStore.delete(id);
				this.requestHandler(deleteRequest);
			});
		}
	}

	insertCache(hash: Array<string>) {
		const tableStore = this.getTableStore('cache');
		if (tableStore) {
			hash.forEach(code => {
				const addRequest = tableStore.add({
					hash: code,
					createdAt: Date.now(),
					updatedAt: Date.now(),
				});
				this.requestHandler(addRequest);
			});
		}
	}

	/**
	 * 检查hash是否存在且在30分钟内
	 * @param hash 要检查的hash值
	 * @returns Promise<boolean>
	 *   - true: hash存在且在30分钟内
	 *   - false: hash不存在或存在但超过30分钟(已删除)
	 */
	existsWithin30Minutes(hash: string): Promise<boolean> {
		return new Promise(resolve => {
			const tableStore = this.getTableStore('cache');
			if (tableStore) {
				const index = tableStore.index('hash_idx');

				// 1. 获取完整记录以检查时间
				const getRequest = index.get(hash);

				getRequest.onsuccess = () => {
					const record = getRequest.result;

					if (!record) {
						// 记录不存在
						resolve(false);
						return;
					}

					// 计算30分钟前的时间戳
					const thirtyMinutesAgo = Date.now() - 30 * 60 * 1000;

					if (record.createdAt >= thirtyMinutesAgo) {
						// 记录存在且在30分钟内
						resolve(true);
					} else {
						// 记录存在但超过30分钟 - 删除它
						const deleteRequest = tableStore.delete(record.id);

						deleteRequest.onsuccess = () => {
							resolve(false);
						};

						deleteRequest.onerror = () => {
							resolve(false);
						};
					}
				};

				getRequest.onerror = () => {
					resolve(false);
				};
			}
		});
	}

	updateById(id: string, params: { [key: string]: any }) {
		const tableStore = this.getTableStore('queue');
		if (tableStore) {
			const request = tableStore?.get(id);
			request.onsuccess = event => {
				const _data = (event.target as IDBRequest).result;
				if (_data) {
					tableStore?.put({
						..._data,
						...params,
						updatedAt: Date.now(),
					});
				}
			};
		}
	}

	getAll(): Promise<Array<any>> {
		return new Promise(resolve => {
			const tableStore = this.getTableStore('queue', 'readonly');
			if (tableStore) {
				const request = tableStore?.getAll();
				request.onsuccess = event => {
					resolve((event.target as IDBRequest).result);
				};
				request.onerror = () => {
					resolve([]);
				};
			}
		});
	}

	getLimit(limit = 20): Promise<Array<any>> {
		return new Promise(resolve => {
			const tableStore = this.getTableStore('queue', 'readonly');
			if (tableStore) {
				const request = tableStore.openCursor();
				const results: Array<any> = [];
				let count = 0;
				request.onsuccess = event => {
					const cursor = (event.target as IDBRequest).result;
					if (cursor && count < limit) {
						results.push(cursor.value);
						count++;
						cursor.continue();
					} else {
						resolve(results);
					}
				};

				request.onerror = () => {
					resolve([]);
				};
			} else {
				resolve([]);
			}
		});
	}
}
let DB: DBUtils;
export default () => {
	if (!DB) {
		DB = new DBUtils();
	}
	return DB;
};
